#include "Node.h"
#include "Object.h"
#include "float.h"



Node::Node(vector<Face*> faces, int level, int facesPerNode)
{
    this->level = level;

    // caso base: hojas del arbol
    if (faces.size() <= facesPerNode) {
        this->left = this->right = NULL;
        this->faces = faces;
        this->box = Box();
        vector<Vertex> vertices = faces[0]->object->vertices;

        for (unsigned int i = 0; i < faces.size(); i++) {
            Face *face = faces[i];
            vector<int> vertexIndices = face->vertexIndices;

            for (unsigned int j = 0; j < vertexIndices.size(); j++) {
                Vertex vertex = vertices[vertexIndices[j]];
                this->box.update(vertex.coord);
            }
        }
    }

    //caso recursivo: dividimos las caras en los dos hijos
    else {
       // cerr << "Creando nodo: caso recursivo. Numero de caras: " << faces.size() << endl;
        axisSplited = bestAxis(faces);
        faces = sortByAxis(faces, axisSplited);
        const unsigned int newSize = faces.size()/2;
        vector<Face*> leftFaces(newSize);

        vector<Face*> rightFaces(faces.size()-newSize);
        {
            int j = 0;

            for (unsigned int i = 0; i < leftFaces.size(); i++, j++) {
                leftFaces[i] = faces[j];
            }

            for (unsigned int i = 0; i < rightFaces.size(); i++, j++) {
                rightFaces[i] = faces[j];
            }
        }
        faces.clear();
        this->left = new Node(leftFaces,level+1,facesPerNode);
        this->right = new Node(rightFaces, level+1,facesPerNode);
        this->box = Box(left->box, right->box);
    }
}

Node::~Node()
{
    if (left) delete left;

    if (right) delete right;
}

Axis Node::bestAxis(vector<Face*> &faces) {

    float xmin, xmax, ymin, ymax, zmin, zmax;

    xmin = ymin = zmin = FLT_MAX;
    xmax = ymax = zmax = FLT_MIN;

    for (unsigned int i = 0; i < faces.size(); i++) {
        Face *face = faces[i];
        Point c = face->center();
        if (c.x < xmin) xmin = c.x;
        if (c.x > xmax) xmax = c.x;
        if (c.y < ymin) ymin = c.y;
        if (c.y > ymax) ymax = c.y;
        if (c.z < zmin) zmin = c.z;
        if (c.z > zmax) zmax = c.z;
    }

    float difx, dify, difz;

    difx = xmax - xmin;
    dify = ymax - ymin;
    difz = zmax - zmin;

    if (difx > dify && difx > difz)
        return X;

    else if (dify > difz)
        return Y;

    else
        return Z;
}





bool compareByX(Face *f1, Face *f2)
{
    return f1->center().x < f2->center().x;
}

bool compareByY(Face *f1, Face *f2)
{
    return f1->center().y < f2->center().y;
}

bool compareByZ(Face *f1, Face *f2)
{
    return f1->center().z < f2->center().z;
}

vector<Face*> sortByAxis(vector<Face*> faces, Axis axis)
{

    switch (axis) {
    case X:
        sort(faces.begin(),faces.end(), compareByX);
        break;

    case Y:
        sort(faces.begin(),faces.end(), compareByY);
        break;

    case Z:
        sort(faces.begin(),faces.end(), compareByZ);
        break;
    }

    return faces;
}

bool Node::hit(const Ray &r, float tmin, float tmax, SurfaceHitRecord &closestHit) const
{
    //pimero comprobamos si la caja englobante contiene el vertice (origen del rayo)
    // o intersecta con la caja

    if (!box.contains(r.origin()) && !box.intersects(r,tmin,tmax))
        return false;

    if (isLeaf()) {
        SurfaceHitRecord lastHit;
        bool hit = false;
        for (unsigned int i = 0; i < faces.size(); i++) {
            Face *face = faces[i];
            if (face->hit(r,tmin,tmax,lastHit)) {
                if (lastHit.t < closestHit.t)
                    closestHit = lastHit;
                hit = true;
            }
            if (lastHit.superIntersect) closestHit.count++;
        }
        return hit;
    }
    else {
        SurfaceHitRecord lefttHitRecord, rightHitRecord;
        bool leftHit, rightHit;

        leftHit = left->hit(r,tmin,tmax,lefttHitRecord);
        rightHit = right->hit(r,tmin,tmax,rightHitRecord);

        if (leftHit && rightHit) {
            if (lefttHitRecord.t < rightHitRecord.t)
                closestHit = lefttHitRecord;
            else
                closestHit = rightHitRecord;

            closestHit.count = lefttHitRecord.count + rightHitRecord.count;
            return true;
        }
        else if (leftHit) {
            closestHit = lefttHitRecord;
            return true;
        }
        else if (rightHit) {
            closestHit = rightHitRecord;
            return true;
        }
        else {
            return false;
        }
    }
}

bool Node::shadowHit(const Ray &r, float tmin, float tmax) const
{
    //pimero comprobamos si la caja englobante contiene el vertice (origen del rayo)
    // o intersecta con la caja

    if (!box.contains(r.origin()) && !box.intersects(r,tmin,tmax))
        return false;

    if (isLeaf()) {
        for (unsigned int i = 0; i < faces.size(); i++) {
            if (faces[i]->shadowHit(r,tmin,tmax)) return true;
        }

        return false;
    }
    //si no es hoja, comprobamos si hay interseccion en el hijo izquierdo
    //si no hay interseccion, miramos en el hijo derecho
    else {
        if (left->shadowHit(r,tmin,tmax)) return true;
        return right->shadowHit(r,tmin,tmax);
    }
}





void Node::Render(int depth)
{
    if (level > depth) return;

    if (isLeaf()) {
        box.Render(1,0,1);
    } else {
        this->left->Render(depth);
        this->right->Render(depth);
        if (depth == level) {
            float r,g,b;
            r = g = b = 0.0;
            switch (axisSplited) {
            case X:
                r = 1.0;
                break;
            case Y:
                g = 1.0;
                break;
            case Z:
                b = 1.0;
                break;
            }

            box.Render(r,g,b);

        }
    }
}

inline bool Node::isLeaf() const
{
    return (this->right == NULL);
}

